package com.zy.explore.license.infrastructure.util;

import com.google.common.collect.Maps;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * description Bean对象操作基本类
 *
 * @author 🇨🇳中国印🇨🇳
 * @date 2022年04月17日 01:33:12
 */
public final class DataBeanUtil {

    public static void copyProperties(Object source, Object target) {
        copyProperties(source, target, false);
    }

    public static void copyProperties(Object source, Object target, boolean ignoreNull) {
        Assert.notNull(source, "source must not be null");
        Assert.notNull(target, "target must not be null");

        BeanWrapper sourceWrapper = new BeanWrapperImpl(source);
        BeanWrapper targetWrapper = new BeanWrapperImpl(target);

        copyBean(source, target, sourceWrapper, targetWrapper, ignoreNull);
    }

    public static <R> R copyOf(R source) {
        return copyOf(source, false);
    }

    public static <R> R copyOf(R source, boolean ignoreNull) {
        Assert.notNull(source, "source must not be null");

        return copyFrom(source, source.getClass(), ignoreNull);
    }

    public static <R> R copyOf(Object source, Class<R> targetClass) {
        return copyOf(source, targetClass, false);
    }

    public static <R> R copyOf(Object source, Class<R> targetClass, boolean ignoreNull) {
        Assert.notNull(source, "source must not be null");
        Assert.notNull(targetClass, "targetClass must not be null");

        return copyFrom(source, targetClass, ignoreNull);
    }

    private static <R> R copyFrom(Object source, Class<?> targetClass, boolean ignoreNull) {
        BeanWrapper sourceWrapper = new BeanWrapperImpl(source);
        BeanWrapper targetWrapper = new BeanWrapperImpl(targetClass);
        @SuppressWarnings("unchecked")
        R target = (R) targetWrapper.getWrappedInstance();

        copyBean(source, target, sourceWrapper, targetWrapper, ignoreNull);

        return target;
    }

    private static void copyBean(Object source, Object target, BeanWrapper sourceWrapper, BeanWrapper targetWrapper, boolean ignoreNull) {
        PropertyDescriptor[] targetProperties = targetWrapper.getPropertyDescriptors();

        for (PropertyDescriptor targetProperty : targetProperties) {
            Method writeMethod = targetProperty.getWriteMethod();
            if (writeMethod == null) {
                continue;
            }
            String propertyName = targetProperty.getName();
            if (!sourceWrapper.isReadableProperty(propertyName)) {
                continue;
            }
            Method readMethod = sourceWrapper.getPropertyDescriptor(propertyName).getReadMethod();
            if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                try {
                    Object value = readMethod.invoke(source);
                    if (ignoreNull && value == null) {
                        continue;
                    }
                    writeMethod.invoke(target, value);
                } catch (Throwable ex) {
                    throw new FatalBeanException("Could not copy property '" + propertyName + "' from source to target", ex);
                }
            }
        }
    }
                 
	/**
     * 将JavaBean对象封装到Map集合当中
     *
     * @param bean       java对象
     * @param ignoreNull 是否忽略值为null的字段
     * @return {@link Map <String, Object> }
     * @throws Exception 异常
     */
    public static Map<String, Object> bean2map(Object bean, boolean ignoreNull) throws Exception {
        //创建Map集合对象
        Map<String, Object> map = Maps.newHashMap();
        //获取对象字节码信息,不要Object的属性
        BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass(), Object.class);
        //获取bean对象中的所有属性
        PropertyDescriptor[] list = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor pd : list) {
            // 获取属性名
            String key = pd.getName();
            // 调用getter()方法,获取内容
            Object value = pd.getReadMethod().invoke(bean);
            //增加到map集合当中 去除null字段
            if (ignoreNull) {
                if (value != null) {
                    map.put(key, value);
                }
            } else {
                map.put(key, value);
            }
        }
        return map;
    }


    /**
     * 将Map集合中的数据封装到JavaBean对象中
     *
     * @param map       集合
     * @param classType 封装javabean对象
     * @throws Exception 异常
     */
    public static <T> T map2bean(Map<String, Object> map, Class<T> classType) throws Exception {
        //采用反射动态创建对象
        T obj = classType.newInstance();
        //获取对象字节码信息,不要Object的属性
        BeanInfo beanInfo = Introspector.getBeanInfo(classType, Object.class);
        //获取bean对象中的所有属性
        PropertyDescriptor[] list = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor pd : list) {
            // 获取属性名
            String key = pd.getName();
            // 获取属性值
            Object value = map.get(key);
            // 调用属性setter()方法,设置到javabean对象当中，只有属性值不为null时去设置
            if (value != null) {
                pd.getWriteMethod().invoke(obj, value);
            }
        }
        return obj;
    }				 

    private DataBeanUtil() {
    }
}